package com.zf.zffencheng.log;

import com.alibaba.fastjson.JSON;
import com.zf.zffencheng.model.Result;
import com.zf.zffencheng.model.pojo.RLog;
import com.zf.zffencheng.model.pojo.RLogDetails;
import com.zf.zffencheng.service.RLogDetailsService;
import com.zf.zffencheng.service.RLogService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;


/**
 * ClassName: RLogAspect <br/>
 * Description:  <br/>
 * date: 2021/5/10 9:41<br/>
 *
 * @author william <br/>
 */
@Log4j2
@Aspect
@Component
public class RLogAspect {

    private Integer id;
    private String url;
    private final RLogService rLogService;
    private final RLogDetailsService rLogDetailsService;

    @Autowired
    public RLogAspect(RLogService rLogService, RLogDetailsService rLogDetailsService) {
        this.rLogService = rLogService;
        this.rLogDetailsService = rLogDetailsService;
    }


    @Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
    public void rLogPoint(){

    }
    @AfterThrowing(throwing = "ex", pointcut = "rLogPoint()")
    public void afterThrowing(Throwable ex){
        String message = ex.getMessage();
        StackTraceElement stackTraceElement = ex.getStackTrace()[0];
        log.error("请求地址:{}", this.url);
        log.error("错误信息:{}", message);
        //错误信息定位
        String className = stackTraceElement.getClassName();
        String methodName = stackTraceElement.getMethodName();
        int lineNumber = stackTraceElement.getLineNumber();
        String concat = className.concat(".").concat(methodName);
        Result error = Result.error(message+ "("+concat+":line_"+lineNumber+")");
        RLogDetails rLogDetails = rLogDetailsService.getById(this.id);
        rLogDetails.setErrorText(message);
        rLogDetails.setResponseText(JSON.toJSONString(error));
        rLogDetailsService.updateById(rLogDetails);

    }

    @AfterReturning(pointcut = "rLogPoint()", returning = "obj")
    public void log(JoinPoint joinPoint, Object obj)
    {
        RLogDetails rLogDetails = rLogDetailsService.getById(this.id);
        rLogDetails.setResponseText(obj != null ? JSON.toJSONString(obj) : "");
        rLogDetailsService.updateById(rLogDetails);

    }

    @Before("rLogPoint()")
    public void before(JoinPoint joinPoint){
        //请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();

        StringBuilder requestLog = new StringBuilder();
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        String method = request.getMethod();
        String ip = request.getRemoteAddr();
        ApiOperation annotation = signature.getMethod().getAnnotation(ApiOperation.class);
        //包名
        String declaringTypeName = signature.getDeclaringTypeName();
        //方法名
        String name = signature.getName();
        this.url = request.getRequestURI();

        requestLog.append("【").append(annotation.value()).append("\t")
                .append("请求信息：").append("URL = {").append(this.url).append("},\t")
                .append("请求方式 = {").append(method).append("},\t")
                .append("请求IP = {").append(ip).append("},\t")
                .append("类方法 = {").append(declaringTypeName).append(".")
                .append(name).append("}】\t");
        log.info("请求日志:{}", requestLog);
        // 参数值
        Object[] args = joinPoint.getArgs();
        // 参数名
        String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        String param = "【%s】";
        StringBuilder requestParam =  new StringBuilder();
        for (int i = 0; i < args.length; i++) {
            String logInfo = "请求参数值:"+ JSON.toJSONString(args[i])+ "，参数名:"+ argNames[i];

            requestParam.append(logInfo);
        }
        //参数
        param = String.format(param, requestParam.toString());
        log.info("请求参数:{}", param);


        RLog rLog = new RLog();
        rLog.setLog(annotation.value());
        rLog.setClassName(declaringTypeName.concat(".").concat(name));
        //请求方式
        rLog.setMethod(method);
        //ip
        rLog.setIp(ip);
        //地址
        rLog.setUrl(this.url);
        rLog.setRequestTime(new Date());
        rLogService.save(rLog);
        RLogDetails rLogDetails = new RLogDetails();
        rLogDetails.setRLogId(rLog.getId());
        //参数
        rLogDetails.setRequestText(param);
        rLogDetailsService.save(rLogDetails);
        this.id = rLogDetails.getId();
    }

}
